﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Atosenet.IO
{
    public class IOManager
    {
        #region 成员

        public event IODataReceivedEventHandler IODataReceived;
        public event IODispatcherErrorOccuredEventHandler IOErrorOccured;
        public event IOManagementEventHander IOManagementChanged;

        public const int IO_NOT_INITIALIZED = -1;
        public const int IO_NOT_FOUND = -2;
        
        #endregion


        #region 初始化

        public IOManager()
        {
            IOs = new IOCollection();
        }

        public bool Initialize(PipelinePool parent)
        {
            Parent = parent;            
            return true;
        }
        
        #endregion


        #region 公开函数

        public bool SendToIO(int id, int hostId, byte[] data)
        {
            BaseIO io = IOs.Find(t => { return t.ID == hostId; });

            if (io == null)
                return false;

            return io.WriteTo(id, data, 0, data.Length) >= 0;
        }

        public bool SendToIO(int id, byte[] data)
        {
            BaseIO io = IOs.Find(t => { return t.ID == id; });

            if (io == null)
                return false;

            return io.Write(data, 0, data.Length) >= 0;
        }
        
        public int Create(IOParameter param)
        {
            BaseIO io = CreateIOFromParameter(param);

            if (InitIO(io))
            {
                this.IOs.Add(io);
                return io.ID;
            }
            return IO_NOT_INITIALIZED;
        }

        public int Destroy(int id)
        {
            BaseIO io = IOs.Find(t => { return t.ID == id; });

            if (io == null)
                return IO_NOT_FOUND;

            DestroyIO(io);

            lock (IOs)
            {
                int index = IOs.IndexOf(io);
                IOs.RemoveAt(index);
                return index;
            }            
        }

        public bool Open(int id)
        {
            BaseIO io = IOs.Find(t => { return t.ID == id; });

            if (io == null)
                return false;

            return io.Open();
        }

        public bool Close(int id)
        {
            BaseIO io = IOs.Find(t => { return t.ID == id; });

            if (io == null)
                return false;

            return io.Close();
        }

        #endregion


        #region 静态函数



        #endregion


        #region 核心函数

        private BaseIO CreateIOFromParameter(IOParameter param)
        {
            BaseIO result = null;

            do
            {
                result = BaseIO.CreateInstance(param);
            }
            while (this.IOs.Exists(io => { return io.ID == result.ID; }));
            OnIOManagementChanged(result.ID, IOManagementType.Created);
            return result;
        }

        private bool InitIO(BaseIO io)
        {
            if (!io.IsInitialized)
                return false;

            io.RawDataReceived -= BaseIO_RawDataReceived;
            io.RawDataReceived += BaseIO_RawDataReceived;
            io.Opened -= BaseIO_IOLinkOpened;
            io.Opened += BaseIO_IOLinkOpened;
            io.Closed -= BaseIO_IOLinkClosed;
            io.Closed += BaseIO_IOLinkClosed;
            io.Failed -= IO_Failed;
            io.Failed += IO_Failed;
            io.ClientIOAdded -= IO_ClientIOAdded;
            io.ClientIOAdded += IO_ClientIOAdded;
            io.ClientIORemoved -= IO_ClientIORemoved;
            io.ClientIORemoved += IO_ClientIORemoved;

            return true;
        }



        private bool DestroyIO(BaseIO io)
        {
            if (io.IsOpened)
                Close(io.ID);

            io.RawDataReceived -= BaseIO_RawDataReceived;
            io.Opened -= BaseIO_IOLinkOpened;
            io.Closed -= BaseIO_IOLinkClosed;
            io.Failed -= IO_Failed;
            io.ClientIOAdded -= IO_ClientIOAdded;
            io.ClientIORemoved -= IO_ClientIORemoved;
            OnIOManagementChanged(io.ID, IOManagementType.Destroyed);
            return true;
        }

        #endregion


        #region 属性

        public IOCollection IOs
        {
            get;
            private set;
        }

        private PipelinePool Parent
        {
            get;
            set;
        }

        public bool IsInitialized
        {
            get { return Parent != null; }
        }

        #endregion


        #region 事件

        private void IO_ClientIORemoved(object sender, IOStatusEventArgs e)
        {
            OnIOManagementChanged(e.ID, e.HostID, IOManagementType.ClientIORemoved);
        }

        private void IO_ClientIOAdded(object sender, IOStatusEventArgs e)
        {
            OnIOManagementChanged(e.ID, e.HostID, IOManagementType.ClientIOAdded);
        }

        private void BaseIO_IOLinkClosed(object sender, IOStatusEventArgs e)
        {
            if (e.IsClientIO)
                OnIOManagementChanged(e.ID, e.HostID, IOManagementType.Closed);
            else
                OnIOManagementChanged(e.ID, IOManagementType.Closed);
        }

        private void BaseIO_IOLinkOpened(object sender, IOStatusEventArgs e)
        {
            if (e.IsClientIO)
                OnIOManagementChanged(e.ID, e.HostID, IOManagementType.Opened);
            else
                OnIOManagementChanged(e.ID, IOManagementType.Opened);
        }

        private void IO_Failed(object sender, IOFailedEventArgs e)
        {
            if (e.IsClientIO)
            {
                IOManagementEventArgs ee = new IOManagementEventArgs(e.ID, e.HostID, DateTime.Now, IOManagementType.ErrorOccured, e.ErrorCode);
                IOManagementChanged(this, ee);
            }
            else
            {
                IOManagementEventArgs ee = new IOManagementEventArgs(e.ID, DateTime.Now, IOManagementType.ErrorOccured, e.ErrorCode);
                IOManagementChanged(this, ee);
            }
        }

        private void BaseIO_RawDataReceived(object sender, RawDataReceivedEventArgs e)
        {
            if (e.IsClientMode)
            {
                if (e.ErrorType != IOErrorType.None)
                {
                    OnIOManagementChanged(e.ID, e.HostID, IOManagementType.ErrorOccured);
                    OnIOErrorOccured(e.ErrorType, e.ID, e.HostID, e.Timestamp);
                }
                else
                {
                    OnIOManagementChanged(e.ID, e.HostID, IOManagementType.ReceivingData);
                    OnIODataReceived(e.Data, e.ID, e.HostID, e.Timestamp);
                }
            }
            else
            {
                if (e.ErrorType != IOErrorType.None)
                {
                    OnIOManagementChanged(e.ID, IOManagementType.ErrorOccured);
                    OnIOErrorOccured(e.ErrorType, e.ID, e.Timestamp);
                }
                else
                {
                    OnIOManagementChanged(e.ID, IOManagementType.ReceivingData);
                    OnIODataReceived(e.Data, e.ID, e.Timestamp);
                }
            }
        }

        protected void OnIODataReceived(byte[] data, int id, DateTime time)
        {
            if (IODataReceived != null)
                IODataReceived(this, new IODataReceivedEventArgs(data, id, time));
        }

        protected void OnIODataReceived(byte[] data, int id, int hostId, DateTime time)
        {
            if (IODataReceived != null)
                IODataReceived(this, new IODataReceivedEventArgs(data, id, hostId, time));
        }

        protected void OnIOErrorOccured(IOErrorType errorType, int id, int hostId, DateTime time)
        {
            if (IOErrorOccured != null)
                IOErrorOccured(this, new IODispatcherErrorOccuredEventArgs(errorType, id, hostId, time));
        }

        protected void OnIOErrorOccured(IOErrorType errorType, int id, DateTime time)
        {
            if (IOErrorOccured != null)
                IOErrorOccured(this, new IODispatcherErrorOccuredEventArgs(errorType, id, time));
        }

        protected void OnIOManagementChanged(int id, int hostId, IOManagementType type)
        {
            if (IOManagementChanged != null)
            {
                IOManagementEventArgs e = new IOManagementEventArgs(id, hostId, DateTime.Now, type);
                IOManagementChanged(this, e);
            }
        }

        protected void OnIOManagementChanged(int id, IOManagementType type)
        {
            if (IOManagementChanged != null)
            {
                IOManagementEventArgs e = new IOManagementEventArgs(id, DateTime.Now, type);
                IOManagementChanged(this, e);
            }
        }

        #endregion


        #region 附加对象

        public class IOCollection : BaseCollection<BaseIO>
        {
            public IOCollection()
                : base()
            { }

            protected override void InsertItem(int index, BaseIO item)
            {
                if (this.Exists((io) => { return io.ID == item.ID; }))
                    return;

                base.InsertItem(index, item);
            }

            public new BaseIO this[int id]
            {
                get
                {
                    return this.Find(io => { return io.ID == id; });
                }
            }
        }

        #endregion
    }


    public delegate void IOManagementEventHander(object sender, IOManagementEventArgs e);

    public class IOManagementEventArgs : EventArgs
    {
        public int ID;
        public int HostID = 0;
        public DateTime Timestamp;
        public IOManagementType EventType;
        public int InfoCode;

        public IOManagementEventArgs(int id, int hostId, DateTime timestamp, IOManagementType type, int infoCode)
        {
            ID = id;
            HostID = hostId;
            Timestamp = timestamp;
            EventType = type;
            InfoCode = infoCode;
        }

        public IOManagementEventArgs(int id, DateTime timestamp, IOManagementType type, int infoCode)
            : this(id, id, timestamp, type, infoCode)
        { }

        public IOManagementEventArgs(int id, int hostId, DateTime timestamp, IOManagementType type)
            : this(id, hostId, timestamp, type, 0)
        { }

        public IOManagementEventArgs(int id, DateTime timestamp, IOManagementType type)
            : this(id, timestamp, type, 0)
        { }

        public bool IsClientMode
        {
            get { return HostID != ID; }
        }
    }

    public enum IOManagementType
    {
        Created = 1,
        Destroyed = 2,
        Opened = 3,
        Closed = 4,
        ReceivingData = 5,
        ErrorOccured = 6,
        ClientIOAdded = 7,
        ClientIORemoved = 8
    }

    public delegate void IODataReceivedEventHandler(object sender, IODataReceivedEventArgs e);

    public class IODataReceivedEventArgs : EventArgs
    {
        public byte[] Data = new byte[] { };
        public int ID;
        public int HostID = 0;
        public DateTime Timestamp;

        public IODataReceivedEventArgs(byte[] data, int id, DateTime time)
        {
            Data = data;
            ID = id;
            HostID = id;
            Timestamp = time;
        }

        public IODataReceivedEventArgs(byte[] data, int id, int hostId, DateTime time)
        {
            Data = data;
            ID = id;
            HostID = hostId;
            Timestamp = time;
        }

        public bool IsClientMode
        {
            get { return HostID != ID; }
        }
    }

    public delegate void IODispatcherErrorOccuredEventHandler(object sender, IODispatcherErrorOccuredEventArgs e);

    public class IODispatcherErrorOccuredEventArgs : EventArgs
    {
        public int ID;
        public int HostID = 0;
        public IOErrorType ErrorType = IOErrorType.None;
        public DateTime Timestamp;

        public IODispatcherErrorOccuredEventArgs(IOErrorType errorType, int id, DateTime time)
        {
            ErrorType = errorType;
            ID = id;
            HostID = id;
            Timestamp = time;
        }

        public IODispatcherErrorOccuredEventArgs(IOErrorType errorType, int id, int hostId, DateTime time)
        {
            ErrorType = errorType;
            ID = id;
            HostID = hostId;
            Timestamp = time;
        }

        public bool IsClientMode
        {
            get { return HostID != ID; }
        }
    }
}
